package boolean_manip

import (
	"fmt"
	"go/ast"
	"go/token"
	"manip_exec"
)

type BoolManipulator struct {
}

var astNodes = make([]ast.Node, 0)

func (m BoolManipulator) ManipulateAST(f ast.Node, index int) (manip_exec.ManiRes, error) {

	astNodes = make([]ast.Node, 0)		//This has to be reset as well

	// Walk through the AST & Build astNodes
	v := astVisitor{}
	ast.Walk(v, f)

	binExprNodes := make([]*ast.BinaryExpr, 0)

	for _, node := range astNodes {
		switch x := node.(type) {
		case *ast.BinaryExpr:
			switch x.Op {
			case token.GTR ,  token.LSS ,  token.LEQ ,  token.GEQ ,  token.EQL ,  token.NEQ :
				binExprNodes = append(binExprNodes, x)
			}
		}
	}

	var random int
	if len(binExprNodes) > 0 {
	random = index % len(binExprNodes)
	} else {		//Force build failure
		astNodes[0] = nil
		retString := "Nothing manipulated"
		result := manip_exec.ManiRes{[]int{}, retString}
		return result, nil		
	}
	// for _, v := range binExprNodes {		//Debugging
	// 	fmt.Println(v)
	// }
	x := binExprNodes[random]
	oldOp := (*x).Op

	if (*x).Op == token.GTR {
		(*x).Op = token.LSS

	} else if (*x).Op == token.LSS {
		(*x).Op = token.GTR

	} else if (*x).Op == token.LEQ {
		(*x).Op = token.GEQ

	} else if (*x).Op == token.GEQ {
		(*x).Op = token.LEQ

	} else if (*x).Op == token.NEQ {
		(*x).Op = token.EQL

	} else if (*x).Op == token.EQL {
		(*x).Op = token.NEQ

	}
	
	retString := fmt.Sprintf("Changed %s to %s in Line: ", oldOp.String(), (*x).Op.String())
	result := manip_exec.ManiRes{[]int{int((*x).Pos())}, retString}
	return result, nil
}

func (m BoolManipulator) String() string {
	return "bool"
}

//Walker Vistor struct
type astVisitor struct {
}

//Walker Visitor function
func (v astVisitor) Visit(node ast.Node) (w ast.Visitor) {
	if node != nil {

		astNodes = append(astNodes, node) //Builds the Node Slice
	}
	return ast.Visitor(v)
}
